package com.bytech.admin.biz.controller.system;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bytech.common.biz.entity.system.SystemSetting;
import com.bytech.common.biz.entity.system.route.*;
import com.bytech.admin.biz.enums.SystemSettingEnum;
import com.bytech.admin.biz.service.systemSetting.SystemSettingService;
import com.bytech.common.biz.entity.base.BaseResult;
import com.bytech.common.biz.enums.ServiceTypeEnum;
import com.bytech.common.config.AppGlobalConstants;
import com.bytech.common.utils.JedisClient;
import com.bytech.common.utils.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.bouncycastle.asn1.dvcs.ServiceType;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author Zhao
 * @Create 2021/8/13 22:36
 * 暂时通过手写路由和断言的名称以及他们的 args 参数键值对来配置这些路由和断言，具体 name 和 args 怎么写 方法如下
 * SpringCloudGateway 内置了很多过滤器和断言工厂可以帮助我们实现大部分的路由和过滤器需求
 * <p>
 * org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory
 * org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory
 * 他们分别有很多实现类，仔细观察他们那些实现类的名称就能发现他们都以实现的接口类的名字作为后缀
 * org.springframework.cloud.gateway.support.NameUtils 这个工具类会处理这些类名，
 * 去掉共同的后缀作为路由器或者断言的 key 也就是我们需要在配置中设置的 name 值。
 * 这些实现类有些还会在内部定义一个 config 类，这个 config 类的属性的值也就是我们需要配置的 args 键值对
 */
@Controller
@RequestMapping("/system/route")
public class GatewayRouteController {

    @Resource
    private JedisClient jedisClient;
    @Resource
    private SystemSettingService systemSettingService;
    @Resource
    private DiscoveryClient discoveryClient;
    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/list")
    public String toRoteList(Model model) {
        LambdaQueryWrapper<SystemSetting> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SystemSetting::getSettingId, SystemSettingEnum.ROUTE_CONFIG.getId()).orderByDesc(SystemSetting::getVersion);
        List<SystemSetting> settings = systemSettingService.list(queryWrapper);
        List<Integer> versions = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(settings)) {
            versions = settings.stream().map(SystemSetting::getVersion).sorted().collect(Collectors.toList());
        }
        model.addAttribute("versions", versions);
        MyVersionableRouteConfigInfo info = new MyVersionableRouteConfigInfo();
        String json = jedisClient.go((jedisCluster -> jedisCluster.get(AppGlobalConstants.GATEWAY_ROUTES_KEY)));
        if (!StringUtils.isBlank(json)) {
            info = JSON.parseObject(json, MyVersionableRouteConfigInfo.class);
        }
        model.addAttribute("curServerVersion", info.getVersion());
        return "/system/route/list";
    }

    @PostMapping("/useConfig")
    @ResponseBody
    public BaseResult<String> useRouteConfig(Integer version) {
        SystemSetting setting = systemSettingService.getBySettingIdAndVersion(SystemSettingEnum.ROUTE_CONFIG.getId(), version);
        if (setting == null) {
            return BaseResult.<String>error("版本不存在").build();
        }
        if (StringUtils.isBlank(setting.getSettingValue())) {
            return BaseResult.<String>error("当前配置为空").build();
        }
        MyVersionableRouteConfigInfo info = new MyVersionableRouteConfigInfo();
        info.setVersion(setting.getVersion());
        info.setId(setting.getSettingId());
        info.setRouteDefinitions(JSON.parseArray(setting.getSettingValue(), MyRouteDefinition.class));
        jedisClient.go((cluster) -> cluster.set(AppGlobalConstants.GATEWAY_ROUTES_KEY, JSON.toJSONString(info)));
        return BaseResult.<String>success("应用成功").build();
    }

    /**
     * 根据版本号查询 没传就查最新的那个
     */
    @GetMapping("/routeInfo")
    @ResponseBody
    public BaseResult<MyVersionableRouteConfigInfo> getRouteConfig(Integer version) {
        SystemSetting setting = null;
        MyVersionableRouteConfigInfo info = new MyVersionableRouteConfigInfo();
        if (version == null) {
            setting = systemSettingService.getLatest(SystemSettingEnum.ROUTE_CONFIG.getId());
        } else {
            setting = systemSettingService.getBySettingIdAndVersion(SystemSettingEnum.ROUTE_CONFIG.getId(), version);
        }
        List<MyRouteDefinition> rs = Lists.newArrayList();
        if (setting != null) {
            rs = JSON.parseArray(setting.getSettingValue(), MyRouteDefinition.class);
            info.setVersion(setting.getVersion());
            info.setId(setting.getSettingId());
            info.setRouteDefinitions(rs);
        }
        return BaseResult.<MyVersionableRouteConfigInfo>success().data(info).build();
    }

    /**
     * 获取某一版本的某一路由 ID 下的配置 注意这个 ID是路由配置的 ID 不是系统设置中代表路由设置的 ID
     */
    @GetMapping("/update")
    public String toUpdateRoute(String id, Integer version, Model model) {
        SystemSetting setting = systemSettingService.getBySettingIdAndVersion(SystemSettingEnum.ROUTE_CONFIG.getId(), version);
        List<MyRouteDefinition> rs = Lists.newArrayList();
        if (setting != null && StringUtils.isNotBlank(setting.getSettingValue())) {
            rs = JSON.parseArray(setting.getSettingValue(), MyRouteDefinition.class);
        }
        if (!CollectionUtils.isEmpty(rs)) {
            MyRouteDefinition routeDefinition = rs.stream().filter(r -> r.getId().equals(id)).findFirst().orElse(new MyRouteDefinition());
            model.addAttribute("routeDefinition", routeDefinition);
        }
        model.addAttribute("curVersion", version);
        return "/system/route/save";
    }

    @GetMapping("/add")
    public String toAddRoute(Model model) {
        model.addAttribute("routeDefinition", new MyRouteDefinition());
        return "/system/route/save";
    }

    @PostMapping("/save")
    @ResponseBody
    public BaseResult<String> saveRouteInfo(@RequestBody MyRouteDefinitionAssemObject assemObject) {
        if (assemObject.getVersion() == null) {
            return BaseResult.<String>error("配置版本不存在").build();
        }
        SystemSetting dbSetting = systemSettingService.getBySettingIdAndVersion(SystemSettingEnum.ROUTE_CONFIG.getId(), assemObject.getVersion());
        if (dbSetting == null) {
            return BaseResult.<String>error("路由配置版本【" + assemObject.getVersion() + "】不存在").build();
        }

        // 组装最新的路由配置对象
        MyRouteDefinition definition = new MyRouteDefinition();
        definition.setId(assemObject.getId());
        definition.setOrder(assemObject.getOrder());
        definition.setUri(assemObject.getUri());

        // 组装路由的过滤器对象
        List<MyFilterDefinition> filters = Lists.newArrayList();
        for (MyRouteDefinitionAssemObject.FilterObject filterObject : assemObject.getFilters()) {
            MyFilterDefinition filterDefinition = new MyFilterDefinition();
            filterDefinition.setName(filterObject.getName());
            Map<String, String> filterArgs = Maps.newHashMap();
            for (MyRouteDefinitionAssemObject.FilterObject.AssmemArgs arg : filterObject.getArgs()) {
                filterArgs.put(arg.getKey(), arg.getValue());
            }
            filterDefinition.setArgs(filterArgs);
            filters.add(filterDefinition);
        }
        definition.setFilters(filters);

        //组装路由的断言对象
        List<MyPredicateDefinition> predicates = Lists.newArrayList();
        for (MyRouteDefinitionAssemObject.PredicateObject predicateObject : assemObject.getPredicates()) {
            MyPredicateDefinition predicateDefinition = new MyPredicateDefinition();
            predicateDefinition.setName(predicateObject.getName());
            Map<String, String> predicateArgs = Maps.newHashMap();
            for (MyRouteDefinitionAssemObject.PredicateObject.AssmemArgs arg : predicateObject.getArgs()) {
                predicateArgs.put(arg.getKey(), arg.getValue());
            }
            predicateDefinition.setArgs(predicateArgs);
            predicates.add(predicateDefinition);
        }
        definition.setPredicates(predicates);

        List<MyRouteDefinition> dbDefinitions = JSON.parseArray(dbSetting.getSettingValue(), MyRouteDefinition.class);
        dbDefinitions.removeIf(e -> e.getId().equals(definition.getId())); // 删掉老的路由ID相关的配置
        dbDefinitions.add(definition); // 添加新的
        dbSetting.setSettingValue(JSON.toJSONString(dbDefinitions)); // 设置更新后的配置到数据库
        dbSetting.setModifyTime(new Date());
        systemSettingService.updateById(dbSetting);
        return BaseResult.<String>success("保存成功！将在一秒后刷新列表").build();
    }

    @PostMapping("/delete")
    @ResponseBody
    public BaseResult<String> doDeleteRoute(String routeId, Integer version) {
        if (StringUtils.isBlank(routeId) || version == null) {
            return BaseResult.<String>error("路由ID或配置版本不能为空").build();
        }
        SystemSetting dbSetting = systemSettingService.getBySettingIdAndVersion(SystemSettingEnum.ROUTE_CONFIG.getId(), version);
        List<MyRouteDefinition> rs = JSON.parseArray(dbSetting.getSettingValue(), MyRouteDefinition.class);
        rs.removeIf(e -> e.getId().equals(routeId));
        dbSetting.setSettingValue(JSON.toJSONString(rs));
        dbSetting.setModifyTime(new Date());
        systemSettingService.updateById(dbSetting);
        return BaseResult.<String>success("删除成功").build();
    }

    @PostMapping("/copyConfig")
    @ResponseBody
    public BaseResult<String> copyRouteConfig(Integer version) {
        SystemSetting setting = systemSettingService.getBySettingIdAndVersion(SystemSettingEnum.ROUTE_CONFIG.getId(), version);
        if (setting == null) {
            return BaseResult.<String>error("未找到此版本的配置文件").build();
        }
        SystemSetting latest = systemSettingService.getLatest(SystemSettingEnum.ROUTE_CONFIG.getId());
        Integer latestVersion = latest.getVersion();
        SystemSetting newSetting = new SystemSetting();
        newSetting.setId(StringUtils.getUUID32());
        newSetting.setSettingId(setting.getSettingId());
        newSetting.setVersion(latestVersion + 1);
        newSetting.setSettingValue(setting.getSettingValue());
        newSetting.setCreateTime(new Date());
        newSetting.setModifyTime(new Date());
        systemSettingService.save(newSetting);
        return BaseResult.<String>success("操作成功！最新的版本号为V" + newSetting.getVersion()).build();
    }

    @PostMapping("/manualUpdate")
    @ResponseBody
    public BaseResult<String> manualUpdateGatewayRoute() {
        List<ServiceInstance> instances = discoveryClient.getInstances(ServiceTypeEnum.GATEWAY_SERVICE.getServiceId());
        int failCount = 0, successCount = 0;
        String failMsg = "";
        if (!CollectionUtils.isEmpty(instances)) {
            for (ServiceInstance instance : instances) {
                String gatewayRouteUpdateUrl = String.format("http://%s:%s%s", instance.getHost(), instance.getPort(), AppGlobalConstants.GATEWAY_ROUTE_MANUAL_UPDATE_URL);
                BaseResult result = restTemplate.getForObject(gatewayRouteUpdateUrl, BaseResult.class);
                if (result == null || result.isFailure()) {
                    failCount++;
                    failMsg += "<br>" + instance.getHost() + ":" + instance.getPort() + "更新失败。";
                } else {
                    successCount++;
                }
            }
        }
        return BaseResult.<String>success("操作成功，成功【" + successCount + "】,失败【" + failCount + "】" + failMsg).build();
    }
}
